﻿/*----------------------------------------------------------------
//  <copyright file="DataAuthorizationManagerBase.cs" company="MicroCloud@151504200868">
//      Copyright © 2020-2024 MicroCloud Corporation, All rights reserved.
//  </copyright>
//  <site>https://gitee.com/chenmm123/microclouds</site>
//  <last-editor>cmm</last-editor>
//  <last-date>2024-03-04 14:44</last-date>
//----------------------------------------------------------------*/

namespace MicroCloud.Authorization
{
    #region "数据权限管理器"
    /// <summary>
    /// 数据权限管理器
    /// </summary>
    /// <typeparam name="TEntityInfo">数据实体类型</typeparam>
    /// <typeparam name="TEntityInfoInputDto">数据实体输入DTO类型</typeparam>
    /// <typeparam name="TEntityRole">实体角色类型</typeparam>
    /// <typeparam name="TEntityRoleInputDto">实体角色输入DTO类型</typeparam>
    /// <typeparam name="TRole">角色类型</typeparam>
    /// <typeparam name="TRoleKey">角色编号类型</typeparam>
    /// <typeparam name="TEntityUser">实体用户类型</typeparam>
    /// <typeparam name="TEntityUserInputDto">实体用户输入DTO类型</typeparam>
    /// <typeparam name="TUser">用户类型</typeparam>
    /// <typeparam name="TUserKey">用户编号类型</typeparam>
    public abstract class DataAuthorizationManagerBase<TEntityInfo, TEntityInfoInputDto, TEntityRole, TEntityRoleInputDto, TRole, TRoleKey, TEntityUser, TEntityUserInputDto, TUser, TUserKey>
        : IEntityInfoStore<TEntityInfo, TEntityInfoInputDto>, IEntityRoleStore<TEntityRole, TEntityRoleInputDto, TRoleKey>, IEntityUserStore<TEntityUser, TEntityUserInputDto, TUserKey>
        where TEntityInfo : IEntityInfo
        where TEntityInfoInputDto : EntityInfoInputDtoBase
        where TEntityRole : EntityRoleBase<TRoleKey>
        where TEntityRoleInputDto : EntityRoleInputDtoBase<TRoleKey>
        where TRole : RoleBase<TRoleKey, TUserKey>
        where TRoleKey : struct, IEquatable<TRoleKey>
        where TEntityUser : EntityUserBase<TUserKey>
        where TEntityUserInputDto : EntityUserInputDtoBase<TUserKey>
        where TUser : UserBase<TUserKey>
        where TUserKey : struct, IEquatable<TUserKey>
    {
        //字段
        private readonly IServiceProvider _provider;

        #region "属性"
        #region "获取 事件总线"
        /// <summary>
        /// 获取 事件总线
        /// </summary>
        protected IEventBus EventBus => _provider.GetRequiredService<IEventBus>();
        #endregion
        #region "获取 过滤表达式服务"
        /// <summary>
        /// 获取 过滤表达式服务
        /// </summary>
        protected IFilterService FilterService => _provider.GetRequiredService<IFilterService>();
        #endregion
        #region "获取 数据实体仓储模型"
        /// <summary>
        /// 获取 数据实体仓储模型
        /// </summary>
        protected IRepository<TEntityInfo, long> EntityInfoRepository => _provider.GetRequiredService<IRepository<TEntityInfo, long>>();
        #endregion
        #region "获取 实体角色仓储模型"
        /// <summary>
        /// 获取 实体角色仓储模型
        /// </summary>
        protected IRepository<TEntityRole, long> EntityRoleRepository => _provider.GetRequiredService<IRepository<TEntityRole, long>>();
        #endregion
        #region "获取 角色仓储模型"
        /// <summary>
        /// 获取 角色仓储模型
        /// </summary>
        protected IRepository<TRole, TRoleKey> RoleRepository => _provider.GetRequiredService<IRepository<TRole, TRoleKey>>();
        #endregion
        #region "获取 实体用户仓储模型"
        /// <summary>
        /// 获取 实体用户仓储模型
        /// </summary>
        protected IRepository<TEntityUser, long> EntityUserRepository => _provider.GetRequiredService<IRepository<TEntityUser, long>>();
        #endregion
        #region "获取 用户仓储模型"
        /// <summary>
        /// 获取 用户仓储模型
        /// </summary>
        protected IRepository<TUser, TUserKey> UserRepository => _provider.GetRequiredService<IRepository<TUser, TUserKey>>();
        #endregion

        #region "获取 实体信息查询数据集 - Implementation of IEntityInfoStore<TEntityInfo,in TEntityInfoInputDto>"
        /// <summary>
        /// 获取 实体信息查询数据集
        /// </summary>
        public IQueryable<TEntityInfo> EntityInfos => EntityInfoRepository.QueryAsNoTracking();
        #endregion
        #region "获取 实体角色信息查询数据集 - Implementation of IEntityRoleStore<TEntityRole,in TEntityRoleInputDto,in TRoleKey>"
        /// <summary>
        /// 获取 实体角色信息查询数据集
        /// </summary>
        public virtual IQueryable<TEntityRole> EntityRoles => EntityRoleRepository.QueryAsNoTracking();
        #endregion
        #region "获取 实体用户信息查询数据集 - Implementation of IEntityUserStore<TEntityUser,in TEntityUserInputDto,in TUserKey>"
        /// <summary>
        /// 获取 实体用户信息查询数据集
        /// </summary>
        public virtual IQueryable<TEntityUser> EntityUsers => EntityUserRepository.QueryAsNoTracking();
        #endregion

        #endregion

        #region "构造函数"
        #region "初始化一个数据权限管理器的新实例"
        /// <summary>
        /// 初始化一个数据权限管理器 <see cref="DataAuthorizationManagerBase{TEntityInfo, TEntityInfoInputDto, TEntityRole, TEntityRoleInputDto, TRole, TRoleKey, TEntityUser, TEntityUserInputDto, TUser, TUserKey}"/> 的新实例
        /// </summary>
        /// <param name="provider">服务提供者</param>
        protected DataAuthorizationManagerBase(IServiceProvider provider)
        {
            _provider = provider;
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IEntityInfoStore<TEntityInfo,in TEntityInfoInputDto>"
        #region "检查实体信息是否存在"
        /// <summary>
        /// 检查实体信息是否存在
        /// </summary>
        /// <param name="predicate">检查谓语表达式</param>
        /// <param name="id">更新的实体信息编号</param>
        /// <returns>实体信息是否存在</returns>
        public virtual Task<bool> CheckEntityInfoExists(Expression<Func<TEntityInfo, bool>> predicate, long id = default)
        {
            return EntityInfoRepository.CheckExistsAsync(predicate, id);
        }
        #endregion
        #region "更新实体信息"
        /// <summary>
        /// 更新实体信息
        /// </summary>
        /// <param name="dtos">包含更新信息的实体信息DTO信息</param>
        /// <returns>业务操作结果</returns>
        public virtual async Task<OperationResult> UpdateEntityInfos(params TEntityInfoInputDto[] dtos)
        {
            Check.Validate<TEntityInfoInputDto, long>(dtos, nameof(dtos));
            var result = await EntityInfoRepository.UpdateAsync(dtos);
            if (result.Succeeded)
            {
                //实体信息缓存刷新事件
                await EventBus?.PublishAsync(new DataCacheRefreshEventData());
            }
            return result;
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IEntityRoleStore<TEntityRole,in TEntityRoleInputDto,in TRoleKey>"
        #region "检查实体角色信息是否存在"
        /// <summary>
        /// 检查实体角色信息是否存在
        /// </summary>
        /// <param name="predicate">检查谓语表达式</param>
        /// <param name="id">实体角色信息编号</param>
        /// <returns>实体角色信息是否存在</returns>
        public virtual Task<bool> CheckEntityRoleExists(Expression<Func<TEntityRole, bool>> predicate, long id = default)
        {
            return EntityRoleRepository.CheckExistsAsync(predicate, id);
        }
        #endregion
        #region "获取指定角色和实体的过滤条件组"
        /// <summary>
        /// 获取指定角色和实体的过滤条件组
        /// </summary>
        /// <param name="roleId">角色编号</param>
        /// <param name="entityId">实体编号</param>
        /// <param name="operation">操作</param>
        /// <returns>过滤条件组</returns>
        public virtual FilterGroup[] GetEntityRoleFilterGroups(TRoleKey roleId, long entityId, DataAuthOperation operation)
        {
            return EntityRoleRepository.QueryAsNoTracking(m => m.RoleId.Equals(roleId) && m.EntityId == entityId && m.Operation == operation)
                .Select(m => m.FilterGroupJson).ToArray().Select(m => m.FromJsonString<FilterGroup>()).ToArray();
        }
        #endregion
        #region "添加实体角色信息"
        /// <summary>
        /// 添加实体角色信息
        /// </summary>
        /// <param name="dtos">要添加的实体角色信息DTO信息</param>
        /// <returns>业务操作结果</returns>
        public virtual async Task<OperationResult> CreateEntityRoles(params TEntityRoleInputDto[] dtos)
        {
            Check.Validate<TEntityRoleInputDto, long>(dtos, nameof(dtos));

            DataAuthCacheRefreshEventData eventData = new();
            OperationResult result = await EntityRoleRepository.InsertAsync(dtos,
                async dto =>
                {
                    var role = RoleRepository.QueryAsNoTracking(o => o.Id.Equals(dto.RoleId)).Select(o => new { o.Id, o.Name }).FirstOrDefault() ?? throw new Exception(I18N.T("角色 {0} 不存在", dto.RoleId));
                    TEntityInfo entityInfo = await EntityInfoRepository.GetAsync(dto.EntityId) ?? throw new Exception(I18N.T("数据实体 {0} 不存在", dto.EntityId));
                    if (await CheckEntityRoleExists(m => m.RoleId.Equals(dto.RoleId) && m.EntityId == dto.EntityId && m.Operation == dto.Operation))
                    {
                        throw new Exception(I18N.T("角色 {0} 和实体 {1} 和操作 {2} 的数据权限规则已存在", role.Name, entityInfo.Name, dto.Operation));
                    }

                    OperationResult checkResult = CheckFilterGroup(dto.FilterGroup, entityInfo);
                    if (!checkResult.Succeeded)
                    {
                        throw new Exception(I18N.T("数据规则验证失败：{0}", checkResult.Message));
                    }

                    if (!dto.IsLocked)
                    {
                        eventData.SetItems.Add(new DataAuthCacheItem()
                        {
                            Type = UserRoleType.Role,
                            Name = role.Id.ToString(),
                            EntityTypeFullName = entityInfo.TypeName,
                            Operation = dto.Operation,
                            FilterGroup = dto.FilterGroup
                        });
                    }
                });
            if (result.Succeeded && eventData.HasData())
            {
                await EventBus?.PublishAsync(eventData);
            }

            return result;
        }
        #endregion
        #region "更新实体角色信息"
        /// <summary>
        /// 更新实体角色信息
        /// </summary>
        /// <param name="dtos">包含更新信息的实体角色信息DTO信息</param>
        /// <returns>业务操作结果</returns>
        public virtual async Task<OperationResult> UpdateEntityRoles(params TEntityRoleInputDto[] dtos)
        {
            Check.Validate<TEntityRoleInputDto, long>(dtos, nameof(dtos));

            DataAuthCacheRefreshEventData eventData = new();
            OperationResult result = await EntityRoleRepository.UpdateAsync(dtos,
                async (dto, entity) =>
                {
                    var role = RoleRepository.QueryAsNoTracking(o => o.Id.Equals(dto.RoleId)).Select(o => new { o.Id, o.Name }).FirstOrDefault();
                    if (role == null)
                    {
                        throw new Exception(I18N.T("角色 {0} 不存在", dto.RoleId));
                    }

                    TEntityInfo entityInfo = await EntityInfoRepository.GetAsync(dto.EntityId);
                    if (entityInfo == null)
                    {
                        throw new Exception(I18N.T("数据实体 {0} 不存在", dto.EntityId));
                    }

                    if (await CheckEntityRoleExists(m => m.RoleId.Equals(dto.RoleId) && m.EntityId == dto.EntityId && m.Operation == dto.Operation, dto.Id))
                    {
                        throw new Exception(I18N.T("角色 {0} 和实体 {1} 和操作 {2} 的数据权限规则已存在", role.Name, entityInfo.Name, dto.Operation));
                    }

                    OperationResult checkResult = CheckFilterGroup(dto.FilterGroup, entityInfo);
                    if (!checkResult.Succeeded)
                    {
                        throw new Exception(I18N.T("数据规则验证失败：{0}", checkResult.Message));
                    }

                    DataAuthCacheItem cacheItem = new()
                    {
                        Type = UserRoleType.Role,
                        Name = role.Id.ToString(),
                        EntityTypeFullName = entityInfo.TypeName,
                        Operation = dto.Operation,
                        FilterGroup = dto.FilterGroup
                    };
                    if (dto.IsLocked)
                    {
                        eventData.RemoveItems.Add(cacheItem);
                    }
                    else
                    {
                        eventData.SetItems.Add(cacheItem);
                    }
                });

            if (result.Succeeded && eventData.HasData())
            {
                await EventBus?.PublishAsync(eventData);
            }

            return result;
        }
        #endregion
        #region "删除实体角色信息"
        /// <summary>
        /// 删除实体角色信息
        /// </summary>
        /// <param name="ids">要删除的实体角色信息编号</param>
        /// <returns>业务操作结果</returns>
        public virtual async Task<OperationResult> DeleteEntityRoles(params long[] ids)
        {
            DataAuthCacheRefreshEventData eventData = new();
            OperationResult result = await EntityRoleRepository.DeleteAsync(ids,
                async entity =>
                {
                    var role = RoleRepository.QueryAsNoTracking(o => o.Id.Equals(entity.RoleId)).Select(o => new { o.Id, o.Name }).FirstOrDefault();
                    TEntityInfo entityInfo = await EntityInfoRepository.GetAsync(entity.EntityId);
                    if (role != null && entityInfo != null)
                    {
                        eventData.RemoveItems.Add(new DataAuthCacheItem()
                        {
                            Type = UserRoleType.Role,
                            Name = role.Id.ToString(),
                            EntityTypeFullName = entityInfo.TypeName,
                            Operation = entity.Operation
                        });
                    }
                });
            if (result.Succeeded && eventData.HasData())
            {
                //移除数据权限缓存
                await EventBus?.PublishAsync(eventData);
            }

            return result;
        }
        #endregion

        #endregion

        #region "方法 - Implementation of IEntityUserStore<TEntityUser,in TEntityUserInputDto,in TUserKey>"
        #region "检查实体用户信息是否存在"
        /// <summary>
        /// 检查实体用户信息是否存在
        /// </summary>
        /// <param name="predicate">检查谓语表达式</param>
        /// <param name="id">实体用户信息编号</param>
        /// <returns>实体用户信息是否存在</returns>
        public virtual Task<bool> CheckEntityUserExists(Expression<Func<TEntityUser, bool>> predicate, long id = default)
        {
            return EntityUserRepository.CheckExistsAsync(predicate, id);
        }
        #endregion
        #region "获取指定用户和实体的过滤条件组"
        /// <summary>
        /// 获取指定用户和实体的过滤条件组
        /// </summary>
        /// <param name="userId">用户编号</param>
        /// <param name="entityId">实体编号</param>
        /// <param name="operation">操作</param>
        /// <returns>过滤条件组</returns>
        public virtual FilterGroup[] GetEntityUserFilterGroups(TUserKey userId, long entityId, DataAuthOperation operation)
        {
            return EntityUserRepository.QueryAsNoTracking(m => m.UserId.Equals(userId) && m.EntityId == entityId && m.Operation == operation)
                .Select(m => m.FilterGroupJson).ToArray().Select(m => m.FromJsonString<FilterGroup>()).ToArray();
        }
        #endregion
        #region "添加实体用户信息"
        /// <summary>
        /// 添加实体用户信息
        /// </summary>
        /// <param name="dtos">要添加的实体用户信息DTO信息</param>
        /// <returns>业务操作结果</returns>
        public virtual async Task<OperationResult> CreateEntityUsers(params TEntityUserInputDto[] dtos)
        {
            Check.Validate<TEntityUserInputDto, long>(dtos, nameof(dtos));

            DataAuthCacheRefreshEventData eventData = new();
            OperationResult result = await EntityUserRepository.InsertAsync(dtos,
                async dto =>
                {
                    var user = UserRepository.QueryAsNoTracking(o => o.Id.Equals(dto.UserId)).Select(o => new { o.Id, o.UserName }).FirstOrDefault();
                    if (user == null)
                    {
                        throw new Exception(I18N.T("用户 {0} 不存在", dto.UserId));
                    }

                    TEntityInfo entityInfo = await EntityInfoRepository.GetAsync(dto.EntityId);
                    if (entityInfo == null)
                    {
                        throw new Exception(I18N.T("数据实体 {0} 不存在", dto.EntityId));
                    }

                    if (await CheckEntityUserExists(m => m.UserId.Equals(dto.UserId) && m.EntityId == dto.EntityId && m.Operation == dto.Operation))
                    {
                        throw new Exception(I18N.T("用户 {0} 和实体 {1} 和操作 {2} 的数据权限规则已存在", user.UserName, entityInfo.Name, dto.Operation));
                    }

                    OperationResult checkResult = CheckFilterGroup(dto.FilterGroup, entityInfo);
                    if (!checkResult.Succeeded)
                    {
                        throw new Exception(I18N.T("数据规则验证失败：{0}", checkResult.Message));
                    }

                    if (!dto.IsLocked)
                    {
                        eventData.SetItems.Add(new DataAuthCacheItem()
                        {
                            Type = UserRoleType.User,
                            Name = user.Id.ToString(),
                            EntityTypeFullName = entityInfo.TypeName,
                            Operation = dto.Operation,
                            FilterGroup = dto.FilterGroup
                        });
                    }
                });
            if (result.Succeeded && eventData.HasData())
            {
                await EventBus?.PublishAsync(eventData);
            }

            return result;
        }
        #endregion
        #region "更新实体用户信息"
        /// <summary>
        /// 更新实体用户信息
        /// </summary>
        /// <param name="dtos">包含更新信息的实体角色信息DTO信息</param>
        /// <returns>业务操作结果</returns>
        public virtual async Task<OperationResult> UpdateEntityUsers(params TEntityUserInputDto[] dtos)
        {
            Check.Validate<TEntityUserInputDto, long>(dtos, nameof(dtos));

            DataAuthCacheRefreshEventData eventData = new();
            OperationResult result = await EntityUserRepository.UpdateAsync(dtos,
                async (dto, entity) =>
                {
                    var user = UserRepository.QueryAsNoTracking(o => o.Id.Equals(dto.UserId)).Select(o => new { o.Id, o.UserName }).FirstOrDefault();
                    if (user == null)
                    {
                        throw new Exception(I18N.T("用户 {0} 不存在", dto.UserId));
                    }

                    TEntityInfo entityInfo = await EntityInfoRepository.GetAsync(dto.EntityId);
                    if (entityInfo == null)
                    {
                        throw new Exception(I18N.T("数据实体 {0} 不存在", dto.EntityId));
                    }

                    if (await CheckEntityUserExists(m => m.UserId.Equals(dto.UserId) && m.EntityId == dto.EntityId && m.Operation == dto.Operation, dto.Id))
                    {
                        throw new Exception(I18N.T("用户 {0} 和实体 {1} 和操作 {2} 的数据权限规则已存在", user.UserName, entityInfo.Name, dto.Operation));
                    }

                    OperationResult checkResult = CheckFilterGroup(dto.FilterGroup, entityInfo);
                    if (!checkResult.Succeeded)
                    {
                        throw new Exception(I18N.T("数据规则验证失败：{0}", checkResult.Message));
                    }

                    DataAuthCacheItem cacheItem = new()
                    {
                        Type = UserRoleType.User,
                        Name = user.Id.ToString(),
                        EntityTypeFullName = entityInfo.TypeName,
                        Operation = dto.Operation,
                        FilterGroup = dto.FilterGroup
                    };
                    if (dto.IsLocked)
                    {
                        eventData.RemoveItems.Add(cacheItem);
                    }
                    else
                    {
                        eventData.SetItems.Add(cacheItem);
                    }
                });

            if (result.Succeeded && eventData.HasData())
            {
                await EventBus?.PublishAsync(eventData);
            }

            return result;
        }
        #endregion
        #region "删除实体用户信息"
        /// <summary>
        /// 删除实体用户信息
        /// </summary>
        /// <param name="ids">要删除的实体用户信息编号</param>
        /// <returns>业务操作结果</returns>
        public virtual async Task<OperationResult> DeleteEntityUsers(params long[] ids)
        {
            DataAuthCacheRefreshEventData eventData = new();
            OperationResult result = await EntityUserRepository.DeleteAsync(ids,
                async entity =>
                {
                    var user = UserRepository.QueryAsNoTracking(o => o.Id.Equals(entity.UserId)).Select(o => new { o.Id, o.UserName }).FirstOrDefault();
                    TEntityInfo entityInfo = await EntityInfoRepository.GetAsync(entity.EntityId);
                    if (user != null && entityInfo != null)
                    {
                        eventData.RemoveItems.Add(new DataAuthCacheItem()
                        {
                            Type = UserRoleType.User,
                            Name = user.Id.ToString(),
                            EntityTypeFullName = entityInfo.TypeName,
                            Operation = entity.Operation
                        });
                    }
                });
            if (result.Succeeded && eventData.HasData())
            {
                //移除数据权限缓存
                await EventBus?.PublishAsync(eventData);
            }

            return result;
        }
        #endregion

        #endregion

        #region "私有静态方法 - Implementation of IEntityRoleStore<TEntityRole,in TEntityRoleInputDto,in TRoleKey>"
        #region "检查实体筛选条件组"
        /// <summary>
        /// 检查实体筛选条件组
        /// </summary>
        /// <param name="group">筛选条件组</param>
        /// <param name="entityInfo">实体信息</param>
        /// <returns>操作结果</returns>
        private OperationResult CheckFilterGroup(FilterGroup group, TEntityInfo entityInfo)
        {
            EntityProperty[] properties = entityInfo.Properties;

            foreach (FilterRule rule in group.Rules)
            {
                EntityProperty property = properties.FirstOrDefault(m => m.Name == rule.Field);
                if (property == null)
                {
                    return OperationResult.Error(I18N.T("属性名 {0} 在实体 {1} 中不存在", rule.Field, entityInfo.Name));
                }

                if (rule.Value == null || rule.Value.ToString().IsNullOrWhiteSpace())
                {
                    return OperationResult.Error(I18N.T("属性名 {0} 操作 {1} 的值不能为空", property.Display, rule.Operate.ToDescription()));
                }
            }

            if (group.Operate == FilterOperate.And)
            {
                List<IGrouping<string, FilterRule>> duplicate = group.Rules.GroupBy(m => m.Field + m.Operate).Where(m => m.Count() > 1).ToList();
                if (duplicate.Count > 0)
                {
                    FilterRule[] rules = duplicate.SelectMany(m => m.Select(n => n)).DistinctBy(m => m.Field + m.Operate, true).ToArray();
                    var rulesString = rules.ExpandAndToString(m => $"{properties.First(n => n.Name == m.Field).Display}-{m.Operate.ToDescription()}", ", ");
                    return OperationResult.Error(I18N.T("组操作为 [并且] 的条件下，字段和操作 {0} 存在重复规则。", rulesString));
                }
            }

            if (group.Groups.Count > 0)
            {
                foreach (FilterGroup g in group.Groups)
                {
                    var result2 = CheckFilterGroup(g, entityInfo);
                    if (!result2.Succeeded)
                    {
                        return result2;
                    }
                }
            }

            Type entityType = Type.GetType(entityInfo.TypeName);
            var result = FilterService.CheckFilterGroup(group, entityType);
            return result;
        }
        #endregion

        #endregion

    }
    #endregion

}
